/*    
    OpenMheg - A free MHEG-5 library
    Copyright (C) 2008 Dharmesh Malam

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Text;
using Core.Model.Actions;
using Core.Model.Data;
using Core.Model.Events;
using System.Collections;
using System.Threading;
using System.IO;
using Core.Model.Data.Generics;
using Engine;
using Core.Model.Ingredients;

namespace Core.Model.Groups
{
    public class Group : Root
    {
        #region Exchanged attributes
        private readonly int [] standardIdentifier = { 2, 19 };    //  2 = Joint ISO ITU, 19 = MHEG

        public int [] StandardIdentifier
        {
            get { return standardIdentifier; }
        }

        private int standardVersion = 1;

        public int StandardVersion
        {
            get { return standardVersion; }
            set { standardVersion = value; }
        }
        private OctetString objectInformation;

        public OctetString ObjectInformation
        {
            get { return objectInformation; }
            set { objectInformation = value; }
        }
        private Core.Model.Actions.Action onStartUp;

        public Core.Model.Actions.Action OnStartUp
        {
            get { return onStartUp; }
            set { onStartUp = value; }
        }
        private Core.Model.Actions.Action onCloseDown;

        public Core.Model.Actions.Action OnCloseDown
        {
            get { return onCloseDown; }
            set { onCloseDown = value; }
        }
        private int originalGroupCachePriority = 127;

        public int OriginalGroupCachePriority
        {
            get { return originalGroupCachePriority; }
            set { originalGroupCachePriority = value; }
        }
        private List<Ingredient> items = new List<Ingredient>();

        public List<Ingredient> Items
        {
            get { return items; }
            set { items = value; }
        }

        #endregion


        #region  Internal Attributes
        private int groupCachePriority;

        internal int GroupCachePriority
        {
            get { return groupCachePriority; }
            set { groupCachePriority = value; }
        }

        private FileInfo groupContentFile;

        public FileInfo GroupContentFile
        {
            get { return groupContentFile; }
            set { groupContentFile = value; }
        }

        #endregion

        #region Implementation specific
        private DateTime startTime;

        public DateTime StartTime
        {
            get { return startTime; }
            set { startTime = value; }

        }

        private Dictionary<int, MHEGTimer> timers = new Dictionary<int, MHEGTimer>();

        internal Dictionary<int, MHEGTimer> Timers
        {
            get { return timers; }
            private set { timers = value; }
        }


        public override void InittInternalAttributes ()
        {
            base.InittInternalAttributes();

            groupCachePriority = originalGroupCachePriority;
        }
        #endregion



        #region Internal Behaviours
        public override void Prepare ()
        {
            if ( !AvailabilityStatus )
            {

                foreach ( Ingredient i in Items )
                {
                    if ( i is Program )
                    {
                        if ( ( ( Program ) i ).InitiallyAvailable )
                            i.Prepare();
                    }
                    else if ( i.InitiallyActive )
                        i.Prepare();
                }

                base.Prepare();
            }

            log.Info( "Prepared: "+ObjectIdentifier+"." );
        }


        public override void Activate ()
        {
            if ( RunningStatus )
                return;

            base.Activate();

            Engine.MHEGEngine.Instance.DoActions( OnStartUp );
            
            foreach ( Ingredient i in items )
                if ( i.InitiallyActive )
                    i.Activate();

            StartTime = System.DateTime.Now;

            RunningStatus = true;
            Engine.MHEGEngine.Instance.Handle( new IsRunning( this ) );

            log.Info("Activated: "+ObjectIdentifier+" with "+Items.Count+" items.");

        }

        public override void Deactivate ()
        {
            if ( !RunningStatus )
                return;

            Engine.MHEGEngine.Instance.DoActions( OnCloseDown );
            
            List<Ingredient> reverse = new List<Ingredient>( items );
            reverse.Reverse();

            foreach ( Ingredient i in reverse )
                i.Deactivate();

            base.Deactivate();

            log.Info( "Deactivated: "+ObjectIdentifier+"." );
        }


        public override void Destroy ()
        {
            List<Ingredient> reverse = new List<Ingredient>( items );
            reverse.Reverse();
            foreach ( Ingredient i in reverse )
                i.Destroy();

            base.Destroy();

            log.Info( "Destroyed: "+ObjectIdentifier+"." );
        }
        #endregion


        #region Actions

        public void SetCachePriority ( int newCachePriority )
        {
            groupCachePriority = newCachePriority;
        }

        public void SetTimer ( GenericInteger timerId, GenericInteger timerValue, GenericBoolean absoluteTime )
        {

            MHEGTimer timer = null;

            if ( timerValue != null && timerValue.Value < 0 )
                return;

            if ( !Timers.ContainsKey( timerId.Value ) )
            {
                if ( timerValue == null )
                    return;

                //  Add the timer
                if ( absoluteTime.Value )
                {
                    DateTime fireTime = StartTime.AddMilliseconds( timerValue.Value );

                    if ( fireTime >= DateTime.Now )
                    {
                        // timer =  new MHEGTimer( timerId.Value, timerValue.Value, absoluteTime.Value, this );
                        // Timers.Add( timerId.Value, timer );

                        TimeSpan future = fireTime.Subtract( DateTime.Now );
                        timer = new MHEGTimer( timerId.Value,
                            ( future.Hours*60*60*1000 + future.Minutes*60*1000 + 
                                future.Seconds*1000+future.Milliseconds ), absoluteTime.Value, this );

                        Timers.Add( timerId.Value, timer );

                        if ( fireTime == DateTime.Now )
                            FireTimer( timer );
                        else
                            Engine.MHEGEngine.Instance.RegisterTimer( timer );
                    }

                }
                else
                {
                    timer =  new MHEGTimer( timerId.Value, timerValue.Value, absoluteTime.Value, this );
                    Timers.Add( timerId.Value, timer );

                    if ( timerValue.Value == 0 )
                        FireTimer( timer );
                    else
                        Engine.MHEGEngine.Instance.RegisterTimer( timer );
                }

            }
            else
            {
                if ( timerValue != null )
                {
                    timer = Timers [timerId.Value];
                    if ( timer.Absolute )
                    {
                        DateTime fireTime = StartTime.AddMilliseconds( timerValue.Value );

                        if ( fireTime >= DateTime.Now )
                        {
                            //timer.FireTime = timerValue.Value;
                            Timers.Add( timerId.Value, timer );

                            TimeSpan future = fireTime.Subtract( DateTime.Now );
                            timer.FireTime = ( future.Hours*60*60*1000 + future.Minutes*60*1000 + 
                                                future.Seconds*1000+future.Milliseconds );

                            if ( fireTime == DateTime.Now )
                                FireTimer( timer );
                            else
                                Engine.MHEGEngine.Instance.RegisterTimer( timer );
                        }

                    }
                    else
                    {
                        timer.FireTime = timerValue.Value;

                        if ( timerValue.Value == 0 )
                            FireTimer( timer );
                        else
                            Engine.MHEGEngine.Instance.RegisterTimer( timer );
                    }


                    //  Engine.MHEGEngine.Instance.UpdateTimer( new MHEGTimer( timerId.Value, timerValue.Value, this ) );
                }
                else
                {
                    Timers.Remove( timerId.Value );
                    log.Info( "Removing timer:"+timerId.Value+"." );
                    //Engine.MHEGEngine.Instance.DeregisterTimer( timerId.Value );

                }
            }


        }

        public void FireTimer ( MHEGTimer timer )
        {
            log.Info( "Fireing timer "+timer.ToString()+"." );
            Engine.MHEGEngine.Instance.AsyncHandle( new TimerFired( timer.Group, timer.Id ) );
            //timers.Remove( timer.Id );
        }

        #endregion


        public override string ToString ()
        {
            return base.ToString() + " : " + ObjectIdentifier.GroupIdentifier;
        }
    }
}
